package edu.pku.sei.mte.utilities;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;

import com.sun.tools.javac.Main;

@SuppressWarnings("unchecked")
public class JavaClassLoader extends ClassLoader{
	private static JavaClassLoader INSTANCE = null;
	private String classpath = "bin";
	private Map<String, Class> ClassMap = new HashMap<String, Class>();
	
	public static JavaClassLoader getInstance(){
		if(INSTANCE == null)
			INSTANCE = new JavaClassLoader(JavaClassLoader.class.getClassLoader());
		return INSTANCE;
	}
	
	private JavaClassLoader(ClassLoader parent){
		super(parent);
	}
	
	public <T> Class<T> getJavaClass(File javafile){
		File path = new File("xbin");
		if(path.exists()==false){
			path.mkdir();
		}
		path.deleteOnExit();
		if(compile(javafile, path)){
			String classname = javafile.getName().substring(0, javafile.getName().lastIndexOf("."));
			if(ClassMap.containsKey(classname))
				return ClassMap.get(classname);
			File classfile = new File(javafile.getParentFile().getAbsolutePath() + File.separator + classname + ".class");
			classfile.deleteOnExit();
			byte[] buf = new byte[(int) classfile.length()];
			FileInputStream in = null;
			try {
				in = new FileInputStream(classfile);
				in.read(buf);
				in.close();
			} catch (Exception e) {
				return null;
			}
			Class<T> ci = (Class<T>) defineClass(null, buf, 0, buf.length);
			ClassMap.put(classname, ci);
			return ci;
		}
		return null;
	}
	
	public <T> T getJavaClassInstance(File javafile){
		Class<T> c = getJavaClass(javafile);
		try {
			return c.newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
	private boolean compile(File javafile, File path){
		String[] cmd = {"-classpath", classpath, "-d", path.getAbsolutePath(), javafile.getAbsolutePath()};
		int val = Main.compile(cmd);
		if (val != 0)
			return false;
		return true;
	}
}
